package pr.lanmu.config.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * 赋值工具类
 *
 * @param <T>
 */
public class B<T> {
    private final T t;

    private B(T t) {
        this.t = t;
    }

    public static <T> B<T> builder(Class<T> clazz) {
        T t = null;
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            t = constructor.newInstance();
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
                 IllegalAccessException e) {
            e.printStackTrace();
        }
        return new B<>(t);
    }

    public static <T> B<T> builder(Class<T> clazz, Class<?>[] pt, Object[] p) {
        T t = null;
        try {

            Constructor<T> constructor = clazz.getDeclaredConstructor(pt);
            constructor.setAccessible(true);
            t = constructor.newInstance(p);
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
                 IllegalAccessException e) {
            e.printStackTrace();
        }
        return new B<>(t);
    }

    public static <T> B<T> builder(Supplier<T> t) {
        return new B<>(t.get());
    }

    public static <T> B<T> builder(T t) {
        return new B<>(t);
    }

    private static <V> void changeStaticFinal(Field field, V val) {
        try {
            field.setAccessible(true);
            Field modifersField = Field.class.getDeclaredField("modifiers");
            modifersField.setAccessible(true);
            modifersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public <V> B<T> set(Consumer<V> fun, V val) {
        fun.accept(val);
        return this;
    }

    public <V> B<T> set(BiConsumer<T, V> fun, V val) {
        fun.accept(t, val);
        return this;
    }

    public <V> B<T> set(String fieldName, V val) {
        Field field;
        try {
            field = t.getClass()
                    .getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            return this;
        }
        changeStaticFinal(field, val);
        try {
            field.set(t, val);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return this;
        }
        return this;
    }

    public <V> T build() {
        return t;
    }
}